package calao.storage.bdb;

import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.OperationStatus;

abstract class BdbCursor
{

	private enum Operation {
		GET_FIRST, GET_NEXT, GET_NEXT_DUP, SEARCH_KEY, SEARCH_KEY_RANGE, 
		SET, DELETE
	}

	protected DatabaseEntry hitKey;
	private DatabaseEntry hitData = new DatabaseEntry();
	int hitCount = 0;
	int fetchCount = 0;

	abstract void close();

	abstract OperationStatus doGetFirst() 
	throws DatabaseException; 

	abstract OperationStatus doGetNext() 
	throws DatabaseException; 

	abstract OperationStatus doGetNextDup() 
	throws DatabaseException; 

	abstract OperationStatus doGetSearchKey(DatabaseEntry key)
	throws DatabaseException; 

	abstract OperationStatus doGetSearchKeyRange(DatabaseEntry key) 
	throws DatabaseException;

	abstract OperationStatus doSet(DatabaseEntry key)
	throws DatabaseException; 

	abstract OperationStatus doDelete()
	throws DatabaseException;

	abstract DatabaseEntry getSearchedKey();

	@Override
	protected void finalize()
	{
		close();
	}

	void setSkipData(boolean on)
	{
		if (hitData!=null) {
			if (on) hitData.setPartial(0, 0, true);
			else hitData.setPartial(false);
		}
	}
	
	boolean getSkipData()
	{
		return hitData!=null? hitData.getPartial() : false;
	}

	DatabaseEntry getHitData()
	{
		return hitData;
	}

	DatabaseEntry getHitKey()
	{
		return hitKey;
	}

	boolean getFirst()
	{
		return doOp(Operation.GET_FIRST, null);
	}

	boolean getNext()
	{
		return doOp(Operation.GET_NEXT, null);
	}

	boolean getNextDup()
	{
		return doOp(Operation.GET_NEXT_DUP, null);
	}

	boolean getSearchKey(DatabaseEntry key)
	{
		return doOp(Operation.SEARCH_KEY, key);
	}

	boolean getSearchKeyRange(DatabaseEntry key)
	{
		return doOp(Operation.SEARCH_KEY_RANGE, key);
	}

	boolean delete()
	{
		return doOp(Operation.DELETE, null);
	}

	boolean set(DatabaseEntry data)
	{
		return doOp(Operation.SET, data);
	}

	boolean doOp(Operation op, DatabaseEntry entry) {
		if (hitData!=null) {
			OperationStatus status = OperationStatus.SUCCESS;
			try {
				switch (op) {
				case GET_FIRST:
					status = doGetFirst();
					break;
				case GET_NEXT:
					status = doGetNext();
					break;
				case GET_NEXT_DUP:
					status = doGetNextDup();
					break;
				case SEARCH_KEY:
					status = doGetSearchKey(entry);
					break;
				case SEARCH_KEY_RANGE:
					status = doGetSearchKeyRange(entry);
					break;
				case SET:
					status = doSet(entry);
					break;
				case DELETE:
					status = doDelete();
					break;
				}
			}
			catch (DatabaseException e) {
				throw new RuntimeException(e);
			}
			if (status==OperationStatus.SUCCESS) {
				hitCount++;
				if (!hitData.getPartial()) fetchCount++;
				return true;
			}
			else {
				hitData = null;
			}
		}
		return false;
	}

}
